home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Language/OS - Multiplatform Resource Library
/
LANGUAGE OS.iso
/
lisp
/
wcl-21.lha
/
wcl-2.1
/
src
/
compiler
/
cross
/
cl-macros.lisp
next >
Wrap
Lisp/Scheme
|
1992-09-10
|
18KB
|
611 lines
;;; (C) Copyright 1990-1992 by Wade L. Hennessey. All rights reserved.
(in-package "W")
(defvar *primary-function-info* (make-hash-table :size 3000))
(defvar *new-function-info* nil)
(defmacro-w defmacro (name lambda-list &body body)
`(define-macro ',name
,(parse-macro-definition name lambda-list nil body)))
(defmacro-w defmacro-w (name lambda-list &body body)
`(define-macro ',name
,(parse-macro-definition name lambda-list nil body)))
(defmacro-w deftype (name lambda-list &body body)
`(define-type
',name
,(parse-macro-definition name lambda-list '* body)))
(defmacro-w deftype-w (name lambda-list &body body)
`(define-type
',name
,(parse-macro-definition name lambda-list '* body)))
(defmacro-w define-compiler-macro (name lambda-list &body body)
`(define-compiler-macro-1 ',name
,(parse-macro-definition name lambda-list nil body)))
;;; Cl macros.
(defmacro-w check-arg-type (arg type position)
`(unless (typep ,arg ,type)
(wta ,arg ,type ,position)))
(defmacro-w defmethod (name lambda-list &body body)
(loop for v in lambda-list
for rest on lambda-list by #'cdr
collect (if (listp v) (first v) v) into requireds
collect (if (listp v) (second v) t) into in-types
when (or (null (cdr rest))
(member v lambda-list-keywords :test #'eq))
return (let ((real-lambda-list (append requireds (cdr rest)))
(real-body (wrap-in-block name body)))
`(define-function ',name :defmethod ',in-types 'nil
',real-body
(named-function ,name
(lambda ,real-lambda-list
,@(wrap-in-block
name
(append (mapcar #'(lambda (var type)
`(check-arg-type
;; HEY! fix index
,var ',type 0))
requireds
in-types)
body))))
(named-function ,name
(lambda ,real-lambda-list ,@real-body))))))
(defun wrap-in-block (block-name decls+body)
(multiple-value-bind (body decls)
(parse-body decls+body)
`((declare ,@decls) (block ,block-name ,@body))))
(defmacro-w defun-1 (name lambda-list &body body)
(let ((real-body (wrap-in-block name body)))
`(define-function ',name :defun 'nil 'nil
',real-body
',nil
(named-function ,name
(lambda ,lambda-list ,@real-body)))))
(defmacro-w defun (name formals &body body)
(cond ((symbolp name)
`(defun-1 ,name ,formals ,@body))
((and (consp name) (eq (car name) 'setf))
`(progn
(defun-1 ,(setf-function-symbol name) ,formals ,@body)
(defsetf ,(second name) ,(cdr formals) (,(car formals))
(list ',(setf-function-symbol name) ,@formals))))
(t (error "~A is not a legal function specifier" name))))
(defmacro-w defvar (name &optional init-form doc-string)
`(define-variable ',name ,init-form ,doc-string :VAR))
(defmacro-w defparameter (name init-form &optional doc-string)
`(define-variable ',name ,init-form ,doc-string :PARAMETER))
(defmacro-w defconstant (name init-form &optional doc-string)
`(define-variable ',name ,init-form ,doc-string :CONSTANT))
;;; HEY! This is an inefficient hack for now...
(defmacro-w destructuring-bind (vars form &body body)
(labels ((walk-vars (expr path)
(if (atom expr)
(if (null expr)
expr
`((,expr ,path)))
(append (walk-vars (car expr) `(car ,path))
(walk-vars (cdr expr) `(cdr ,path))))))
(let ((f (gensym "FORM-")))
`(let ((,f ,form))
(let ,(walk-vars vars f) ,@body)))))
(defmacro-w setf (&rest pairs)
`(progn
,@(loop for rest on pairs by #'cddr
collect (let ((place (macroexpand-w (first rest)))
(value (second rest)))
(multiple-value-bind (tvars vals svars store access)
(get-setf-method-w place)
(declare (ignore access))
(let* ((stores (mapcar #'list svars (list value)))
(tmps (mapcar #'list tvars vals)))
`(let* (,@stores
,@tmps)
,store)))))))
(defvar *setf-methods* (make-hash-table :test #'eq))
(defmacro-w define-setf-method (accessor lambda-list &body body)
`(define-setf ',accessor
,(parse-macro-definition accessor lambda-list nil body)))
(defmacro-w defsetf (accessor name-or-args &rest stuff)
(if (null stuff)
`(define-setf ',accessor ',name-or-args)
(let ((updater (destructuring-bind ((value-var) . body) stuff
`(apply #'(lambda (,value-var ,@name-or-args)
,@body)
svar
tvars))))
`(define-setf ',accessor
#'(lambda (access env)
(let ((svar (gensym "S"))
(tvars (loop for arg in (cdr access)
collect (gensym "T"))))
(values tvars
(cdr access)
(list svar)
,updater
`(,(first access) ,@tvars))))))))
(defmacro defsetf-w (accessor name-or-args &rest stuff)
(let ((updater (if (null stuff)
`(list* ',name-or-args (append tvars (list svar)))
(destructuring-bind ((value-var) . body) stuff
`(apply #'(lambda (,value-var ,@name-or-args)
,@body)
svar
tvars)))))
`(define-setf ',accessor
#'(lambda (access env)
(let ((svar (gensym "S"))
(tvars (loop for arg in (cdr access) collect (gensym "T"))))
(values tvars
(cdr access)
(list svar)
,updater
`(,(first access) ,@tvars)))))))
(defun get-setf-method-w (form &optional env)
;; HEY! This is a hack. The correct version of this lives in
;; the library.
(let ((access (macroexpand-w form env)))
(etypecase access
(symbol (let ((svar (gensym "S")))
(values nil nil (list svar) `(setq ,access ,svar) access)))
(list (let ((expander (gethash (car access) *setf-methods*)))
(if (null expander)
(error "No SETF method found for ~A" access)
(if (symbolp expander)
(let ((svar (gensym "S"))
(tvars (loop for arg in (cdr access)
collect (gensym "T"))))
(values tvars
(cdr access)
(list svar)
`(,expander ,@tvars ,svar)
(cons (first access) tvars)))
(funcall expander access env))))))))
(defun define-setf (accessor updater)
(setf (gethash accessor *setf-methods*) updater)
accessor)
(defsetf-w car set-car)
(defsetf-w cdr set-cdr)
(defsetf-w first set-car)
(defsetf-w second (x) (new-value)
`(set-car (cdr ,x) ,new-value))
(defsetf-w third (x) (new-value)
`(set-car (cddr ,x) ,new-value))
(defsetf-w fourth (x) (new-value)
`(set-car (cdddr ,x) ,new-value))
(defsetf-w fifth (x) (new-value)
`(set-car (cddddr ,x) ,new-value))
(defsetf-w sixth (x) (new-value)
`(set-car (nthcdr 5 ,x) ,new-value))
(defsetf-w seventh (x) (new-value)
`(set-car (nthcdr 6 ,x) ,new-value))
(defsetf-w eigth (x) (new-value)
`(set-car (nthcdr 7 ,x) ,new-value))
(defsetf-w ninth (x) (new-value)
`(set-car (nthcdr 8 ,x) ,new-value))
(defsetf-w tenth (x) (new-value)
`(set-car (nthcdr 9 ,x) ,new-value))
(defsetf-w aref (array &rest indices) (new-value)
`(set-aref ,new-value ,array ,@indices))
(defsetf-w sbit (array &rest indices) (new-value)
`(set-sbit ,new-value ,array ,@indices))
(defsetf-w svref set-svref)
(defsetf-w schar set-schar)
(defsetf-w 32bit-vref set-32bit-vref)
(defsetf-w symbol-value set)
(defsetf-w symbol-function set-symbol-function)
(defsetf-w symbol-plist set-symbol-plist)
(defsetf-w symbol-package set-symbol-package)
(defsetf-w symbol-hash-code set-symbol-hash-code)
(defsetf-w get (symbol indicator) (new-value)
`(progn (set-get ,symbol ,indicator ,new-value)
,new-value))
(defsetf-w fill-pointer set-fill-pointer)
;;; HEY! This isn't quite right.....see the manual
;;;(defmacro-w define-modify-macro (name lambda-list function &optional docstr)
;;; `(defmacro-w ,name (reference . ,lambda-list)
;;; (setf ,reference (,function reference ,lambda-list-args))))
;;;(define-modify-macro incf (&optional (delta 1)) +)
(defmacro-w incf (ref &optional (delta 1))
`(setf ,ref (+ ,ref ,delta)))
(defmacro-w decf (ref &optional (delta 1))
`(setf ,ref (- ,ref ,delta)))
(defmacro-w remf (place indicator)
`(setf ,place (delete-property ,place ,indicator)))
(defmacro-w pop (var)
(let ((list (gensym "LIST")))
`(let ((,list ,var))
(prog1 (car ,list)
(setf ,var (cdr ,list))))))
(defmacro-w push (value-form var)
(let ((value (gensym "VALUE")))
`(let ((,value ,value-form))
(setf ,var (cons ,value ,var)))))
(defmacro-w return (&optional (value nil))
`(return-from nil ,value))
(defmacro-w when (pred &rest args)
`(if ,pred
(progn ,@args)
nil))
(defmacro-w unless (pred &rest args)
`(if (not ,pred)
(progn ,@args)
nil))
(defmacro-w psetq (&rest vars+vals)
(let* ((vars (every-even vars+vals))
(vals (every-odd vars+vals))
(tmps (n-list (length vars) #'(lambda () (gensym "TMP")))))
`(let ,(mapcar #'list tmps vals)
(setq ,@(mapcan #'list vars tmps))
nil)))
;;; HEY! change to use (end . result) no the destructuring-bind works.
(defmacro-w do (step-forms (end &rest result) &body decls+body)
(let ((vars (mapcar #'first step-forms))
(inits (mapcar #'second step-forms))
(test-label (gensym "TEST"))
(loop-label (gensym "LOOP")))
(multiple-value-bind (body decls)
(parse-body decls+body)
`(block nil
(let ,(mapcar #'list vars inits)
(declare ,@decls)
(tagbody (go ,test-label) ; loop inversion
,loop-label
(psetq ,@(mapcan #'(lambda (unit)
(if (null (cddr unit)) ; no step form?
nil
(list (first unit) (third unit))))
step-forms))
,test-label
(if ,end
(return (progn ,@result)))
,@body
(go ,loop-label)))))))
;;; HEY! Unify with above?
(defmacro-w do* (step-forms (end &rest result) &body decls+body)
(let ((vars (mapcar #'first step-forms))
(inits (mapcar #'second step-forms))
(test-label (gensym "TEST"))
(loop-label (gensym "LOOP")))
(multiple-value-bind (body decls)
(parse-body decls+body)
`(block nil
(let* ,(mapcar #'list vars inits)
(declare ,@decls)
(tagbody (go ,test-label) ; loop inversion
,loop-label
(setq ,@(mapcan #'(lambda (unit)
(if (null (cddr unit)) ; no step form?
nil
(list (first unit) (third unit))))
step-forms))
,test-label
(if ,end
(return (progn ,@result)))
,@body
(go ,loop-label)))))))
(defmacro-w dotimes ((var limitform &optional result) &body body)
`(loop for ,var from 0 below ,limitform do (progn ,@body)
finally (return ,result)))
; (let ((limit (gensym "LIMIT")))
; `(do ((,limit ,limitform)
; (,var 0 (+ ,var 1)))
; ((= ,var ,limit) ,result)
; (declare (fixnum ,limit ,var))
; ,@body)))
(defmacro-w dolist ((var listform &optional (result nil)) &body body)
`(loop for ,var in ,listform do (progn ,@body) finally (return ,result)))
(defmacro-w prog1 (first &body body)
(let ((value (gensym "VALUE")))
`(let ((,value ,first))
,@body
,value)))
(defmacro-w loop (&whole form)
(macroexpand form))
(defmacro-w prog2 (first second &body body)
(let ((ignore (gensym "TMP"))
(value (gensym "VALUE")))
`(let ((,ignore ,first))
(let ((,value ,second))
,@body
,value))))
(defmacro-w prog (var-list &body body+decls)
(multiple-value-bind (body decls)
(parse-body body+decls)
`(block nil
(let ,var-list
(declare ,@decls)
(tagbody ,@body)))))
(defmacro-w and (&rest args)
(if (null args)
t
(if (null (rest args))
(first args)
`(if ,(first args)
(and ,@(rest args))
nil))))
(defmacro-w or (&rest args)
(if (null args)
nil
(if (null (rest args))
(macroexpand-w (first args))
(let ((arg (gensym "G")))
`(let ((,arg ,(first args)))
(if ,arg
,arg
(or ,@(rest args))))))))
(defmacro-w cond (&rest clauses)
(if (null clauses)
nil
(let ((clause (first clauses)))
(let ((test (first clause))
(body (rest clause)))
`(if ,test
,(if (null body)
nil
`(progn ,@body))
(cond ,@(rest clauses)))))))
(defmacro-w locally (&rest forms)
`((lambda () ,@forms)))
(defmacro-w let (bindings &body body+decls)
(multiple-value-bind (body decls)
(parse-body body+decls)
(if (and (null bindings) (null decls))
`(progn ,@body)
`((lambda ,(mapcar #'(lambda (spec)
(if (atom spec)
spec
(first spec)))
bindings)
(declare ,@decls)
,@body)
,@(mapcar #'(lambda (spec)
(if (atom spec)
'nil
(second spec)))
bindings)))))
(defmacro-w let* (bindings &body body+decls)
(multiple-value-bind (body decls)
(parse-body body+decls)
(if (null bindings)
(if (null decls)
`(progn ,@body)
`(locally ,@body+decls))
(let ((first-binding (if (atom (first bindings))
(list (first bindings) nil)
(first bindings))))
`((lambda ,(if (null bindings)
nil
(list (first first-binding)))
(let* ,(rest bindings) (declare ,@decls) ,@body))
,(second first-binding))))))
(defmacro-w multiple-value-bind (lambda-list values-form &body body)
`(mv-bind ,lambda-list ,values-form ,@body))
(defmacro-w multiple-value-list (values-form)
`(multiple-value-call #'(lambda (&rest l) l) ,values-form))
(defmacro-w multiple-value-setq (vars form)
(let ((tmps (mapcar #'(lambda (x)
(declare (ignore x))
(gensym "TMP")) vars)))
`(multiple-value-call #'(lambda (&optional ,@tmps)
,@(loop for v in vars
for tmp in tmps
collect `(setq ,v ,tmp)))
,form)))
;;; HEY! This would be more efficient as a special form
(defmacro-w multiple-value-prog1 (first-form &rest other-forms)
(let ((value-holder (gensym "MV")))
`(multiple-value-call #'(lambda (&rest ,value-holder)
(progn ,@other-forms
(values-list ,value-holder)))
,first-form)))
(defmacro-w select (key-form &rest cases)
(let ((key (gensym "KEY")))
`(let ((,key ,key-form))
(cond ,@(loop for (case . consequent) in cases
collect (cons (if (member case '(t otherwise))
t
(if (atom case)
`(eql ,key ,case)
`(or ,@(loop for c in case
collect `(eq ,key ,c)))))
consequent))))))
(defmacro-w case (key-form &rest cases)
(let ((key (gensym "KEY")))
`(let ((,key ,key-form))
(cond ,@(loop for (case . consequent) in cases
collect (cons (if (member case '(t otherwise))
t
(list (if (atom case)
'eql
'member)
key
`(quote ,case)))
consequent))))))
(defmacro-w ecase (key &rest cases)
`(case ,key
,@cases
(t (error "~S is not one of the following constants:~{ ~A~}"
,key
',(collect-cases cases)))))
(defmacro-w typecase (key &rest cases)
(let ((k (gensym "KEY")))
`(let ((,k ,key))
,(if (and (eq (caar cases) t) ; single T case?
(null (cdr cases)))
`(progn ,@(cdar cases))
`(cond ,@(loop for (type . consequent) in cases
collect (if (member type '(t otherwise))
`(t ,@consequent)
`((typep ,k ',type) ,@consequent))))))))
(defmacro-w etypecase (key &rest cases)
(let ((k (gensym "KEY")))
`(let ((,k ,key))
(typecase ,k
,@cases
(t (error "~S is not one of these types:~{ ~A~}"
,k
',(collect-cases cases)))))))
(defmacro-w with-open-file ((stream name &rest options) &body body)
`(let ((,stream nil))
(unwind-protect (progn (setq ,stream (open ,name ,@options))
,@body)
(unless (null ,stream)
(close ,stream)))))
(defmacro-w shiftf (&rest args &environment env)
"One or more SETF-style place expressions, followed by a single
value expression. Evaluates all of the expressions in turn, then
assigns the value of each expression to the place on its left,
returning the value of the leftmost."
(if (< (length args) 2)
(error "Too few argument forms to a SHIFTF."))
(let ((leftmost (gensym)))
(do ((a args (cdr a))
(let-list nil)
(setf-list nil)
(next-var leftmost))
((atom (cdr a))
(push (list next-var (car a)) let-list)
`(let* ,(nreverse let-list) ,@(nreverse setf-list) ,leftmost))
(multiple-value-bind (dummies vals newval setter getter)
(get-setf-method-w (car a) env)
(do* ((d dummies (cdr d))
(v vals (cdr v)))
((null d))
(push (list (car d) (car v)) let-list))
(push (list next-var getter) let-list)
(push setter setf-list)
(setq next-var (car newval))))))
(defmacro-w rotatef (&rest args &environment env)
"Takes any number of SETF-style place expressions. Evaluates all of the
expressions in turn, then assigns to each place the value of the form to
its right. The rightmost form gets the value of the leftmost. Returns NIL."
(cond ((null args) nil)
((null (cdr args)) `(progn ,(car args) nil))
(t (do ((a args (cdr a))
(let-list nil)
(setf-list nil)
(next-var nil)
(fix-me nil))
((atom a)
(rplaca fix-me next-var)
`(let* ,(nreverse let-list) ,@(nreverse setf-list) nil))
(multiple-value-bind (dummies vals newval setter getter)
(get-setf-method-w (car a) env)
(do ((d dummies (cdr d))
(v vals (cdr v)))
((null d))
(push (list (car d) (car v)) let-list))
(push (list next-var getter) let-list)
;; We don't know the newval variable for the last form yet,
;; so fake it for the first getter and fix it at the end.
(unless fix-me (setq fix-me (car let-list)))
(push setter setf-list)
(setq next-var (car newval)))))))
(defmacro-w pushnew (item place &key (test '#'eql) test-not (key '#'car))
`(setf ,place (adjoin/4 ,item ,place ,test ,key)))
(defmacro-w loop-finish ()
'(go end-loop))
(defmacro-w declaim (&rest decl-specs)
`(progn ,@(loop for spec in decl-specs collect `(proclaim ',spec))))
(defmacro-w defun-inline (name &rest stuff)
`(progn (declaim (inline ,name))
(defun ,name ,@stuff)))
(defmacro-w defmethod-inline (name &rest stuff)
`(progn (declaim (inline ,name))
(defmethod ,name ,@stuff)))
(defmacro-w backquote (x)
(bq-completely-process x))